﻿// ***********************************************************************
// Assembly         : GooglePredict
// Author           : aj
// Created          : 10-08-2010
//
// Last Modified By : aj
// Last Modified On : 10-08-2010
// Description      : 
//
//
// ***********************************************************************
using System.Text;

namespace Google.API.Predict.Requests.Core
{
    using System;
    using System.IO;
    using System.Linq;
    using System.Net;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using Responses;

    /// <summary>
    /// Abstracted, generic API request class
    /// HTTP request generation and response parsing
    /// </summary>
    /// <typeparam name="T">Response Type - See Google.API.Predict.Responses</typeparam>
    public abstract class GooglePredictionApiRequest<T> : IGooglePredictionApiRequest where T : BaseResponse, new()
    {
        /// <summary>
        /// Base api url
        /// </summary>
        public const string ApiUri = "https://www.googleapis.com/prediction/v1.1/training";

        protected GooglePredictionApiRequest()
        {
        }

        internal GooglePredictionApiRequest(string auth, string bucket, string data)
        {
            Auth = auth;
            Bucket = bucket;
            Data = data;
        }

        public enum HttpMethod
        {
            /// <summary>
            /// HTTP GET
            /// </summary>
            Get,

            /// <summary>
            /// HTTP POST
            /// </summary>
            Post,

            /// <summary>
            /// HTTP DELETE
            /// </summary>
            Delete
        }

        #region IGooglePredictRequest Members

        /// <summary>
        /// Auth token to send with the request
        /// </summary>
        public string Auth
        {
            get;
            set;
        }

        /// <summary>
        /// Bucket the request is about
        /// </summary>
        public string Bucket
        {
            get;
            set;
        }

        /// <summary>
        /// Data file within bucket the request is about
        /// </summary>
        public string Data
        {
            get;
            set;
        }

        /// <summary>
        /// HTTP Method to be used
        /// </summary>
        public HttpMethod Method
        {
            get;
            protected set;
        }

        /// <summary>
        /// Data to post with the request
        /// </summary>
        public System.Collections.ObjectModel.ReadOnlyCollection<byte> PostData
        {
            get;
            protected set;
        }

        /// <summary>
        /// Response from the request
        /// </summary>
        public T Response
        {
            get;
            protected set;
        }

        /// <summary>
        /// Request format for the prediction api
        /// </summary>
        protected string RequestFormat { get; set; }

        /// <summary>
        /// Prepares a request for transmission
        /// </summary>
        /// <returns>WebRequest ready for transmission</returns>
        protected virtual WebRequest PrepareRequest()
        {
            var requestUrl = string.Format(System.Globalization.CultureInfo.InvariantCulture, RequestFormat, ApiUri, Bucket, Data);

            // BUILD THE URI AND FIXUP IN CASE OF ESCAPED SLASHES
            var uri = new Uri(requestUrl);
            uri.LeaveDotsAndSlashesEscaped();

            var request = (HttpWebRequest)WebRequest.Create(uri);
            request.KeepAlive = false;
            // ORDER OF HEADERS IS VITAL - THIS MUST COME FIRST
            request.Headers.Add(HttpRequestHeader.Authorization, "GoogleLogin auth=" + Auth);
            request.ContentType = "application/json";

            request.Method = Method.ToString().ToUpper();

            switch (Method)
            {
                case HttpMethod.Post:
                    {
                        request.ContentLength = PostData.Count;

                        var stream = request.GetRequestStream();
                        stream.Write(PostData.ToArray(), 0, PostData.Count);
                        break;
                    }
                default:
                    {                        
                        break;
                    }
            }            
            return request;
        }

        /// <summary>
        /// Executes the request - never throws an error - check the response object for errors
        /// </summary>
        /// <returns>JSON parses as JOBject or null if empty</returns>
        internal virtual JObject Execute()
        {
            try
            {
                var request = PrepareRequest();
                var response = request.GetResponse();

                if (response != null)
                {
                    var json = new StreamReader(response.GetResponseStream()).ReadToEnd();
                    response.Close();

                    Response = new T
                                   {
                                       Success = true,
                                       ResponseStatusCode = ((HttpWebResponse)response).StatusCode,
                                       ResponseStatusCodeDescription = ((HttpWebResponse)response).StatusDescription
                                   };

                    if (!string.IsNullOrEmpty(json))
                    {
                        try
                        {
                            var result = JObject.Parse(json);
                            Response = JsonConvert.DeserializeObject<T>(result["data"].ToString());
                            Response.Success = true;
                            return result;
                        }
                        catch (Exception e)
                        {
                            Response = new T {InnerException = e, Success = false};
                            return null;
                        }
                    }
                }

                return null;
            }
            catch (WebException err)
            {
                var badResponse = err.Response as HttpWebResponse;
                Response = badResponse != null ? new T { Success = false, ResponseStatusCode = badResponse.StatusCode, ResponseStatusCodeDescription = badResponse.StatusDescription, InnerException = err } : new T { Success = false, InnerException = err };

                return null;
            }
        }

        #endregion
    }
}
